﻿
Imports std

Public Module performance
    Public Const roundinterval As Int64 = 60 * 10
    Public Const defaultConfigfile As String = "performance.ini"
    Private WithEvents config As configMonitor = Nothing
    Private perfcounter As PerformanceCounter = Nothing
    Private keepalive As Boolean = configNaming.defaultValue.performance.keepAlive
    Private writeConsole As Boolean = configNaming.defaultValue.performance.writeConsole
    Private writeCounter As Boolean = configNaming.defaultValue.performance.writeCounter

    Public Class recordThreshold
        Public Shared process As vector(Of String) = Nothing
        Public Shared processor As vector(Of Double) = Nothing
        Public Shared workingSet As vector(Of Double) = Nothing
        Public Shared virtualBytes As vector(Of Double) = Nothing
    End Class

    Public Class killThreshold
        Public Shared process As vector(Of String) = Nothing
        Public Shared processor As vector(Of Double) = Nothing
        Public Shared workingSet As vector(Of Double) = Nothing
        Public Shared virtualBytes As vector(Of Double) = Nothing
    End Class

    Private Sub writeImpl(ByVal name As String, ByVal value As Double, _
                          Optional ByVal memorysize As Boolean = False)
        If writeConsole Then
            If Not memorysize Then
                writeConsoleline(name + character.blank + strleft(Convert.ToString(value), 6))
            Else
                writeConsoleline(name + character.blank + memorySizeDisplay(value))
            End If
        End If
        If writeCounter Then
            Dim cname As String = Nothing
            Dim cvalueDouble As Double = 0
            Dim cvalue As Int64 = 0
            If between(value, 0, 1) Then
                cname = name + character.underline + "*1000"
                cvalueDouble = value * 1000
            Else
                cname = name
                cvalueDouble = value
            End If
            cvalue = Convert.ToInt64(cvalueDouble)
            strrplc(cname, character.blank, character.underline)
            counter2.increaseLastAverage(strtoupper(cname), cvalue,
                                         lastAverageLength:=1, autoInitialize:=True)
        End If
    End Sub

    Private Sub writeperfvalue(ByVal perfcounter As PerformanceCounter _
                               , Optional ByVal catname As String = Nothing _
                               , Optional ByVal countername As String = Nothing _
                               , Optional ByVal memorysize As Boolean = False _
                               , Optional ByVal threshold As Single = npos)
        If isemptystring(catname) Then
            catname = perfcounter.CategoryName + character.numberMark + perfcounter.InstanceName
        End If
        If isemptystring(countername) Then
            countername = perfcounter.CounterName
        End If

        Dim value As Single = 0
        Dim round As Int32 = 0
        perfcounter.NextValue()
        sleep(64)
        Do
            value = perfcounter.NextValue()
            sleep(64)
            round += 1
        Loop Until value > 0 OrElse round >= 16
        If threshold = npos OrElse value >= threshold Then
            writeImpl(catname + character.blank + countername, value, memorysize)
        End If
    End Sub

    Private Function readconfig() As Boolean
        keepalive = config.parameter(configNaming.performance.name, configNaming.performance.keepAlive, _
                                     configNaming.defaultValue.performance.keepAlive)
        writeConsole = config.parameter(configNaming.performance.name, configNaming.performance.writeConsole, _
                                        configNaming.defaultValue.performance.writeConsole)
        writeCounter = config.parameter(configNaming.performance.name, configNaming.performance.writeCounter, _
                                        configNaming.defaultValue.performance.writeCounter)

        config.readlist(configNaming.killHeavyProcess.name _
               , configNaming.killHeavyProcess.process _
               , killThreshold.process)
        config.readlist(configNaming.killHeavyProcess.name _
                        , configNaming.killHeavyProcess.processor _
                        , killThreshold.processor)
        config.readlist(configNaming.killHeavyProcess.name _
                        , configNaming.killHeavyProcess.workingSet _
                        , killThreshold.workingSet)
        config.readlist(configNaming.killHeavyProcess.name _
                        , configNaming.killHeavyProcess.virtualBytes _
                        , killThreshold.virtualBytes)

        config.readlist(configNaming.recordHeavyProcess.name _
                        , configNaming.recordHeavyProcess.process _
                        , recordThreshold.process)
        config.readlist(configNaming.recordHeavyProcess.name _
                        , configNaming.recordHeavyProcess.processor _
                        , recordThreshold.processor)
        config.readlist(configNaming.recordHeavyProcess.name _
                        , configNaming.recordHeavyProcess.workingSet _
                        , recordThreshold.workingSet)
        config.readlist(configNaming.recordHeavyProcess.name _
                        , configNaming.recordHeavyProcess.virtualBytes _
                        , recordThreshold.virtualBytes)

        Return killThreshold.process.size() = killThreshold.processor.size() _
                AndAlso killThreshold.process.size() = killThreshold.workingSet.size() _
                AndAlso killThreshold.process.size() = killThreshold.virtualBytes.size() _
                AndAlso recordThreshold.process.size() = recordThreshold.processor.size() _
                AndAlso recordThreshold.process.size() = recordThreshold.workingSet.size() _
                AndAlso recordThreshold.process.size() = recordThreshold.virtualBytes.size()
    End Function

    Private Function loadconfig(ByVal configfile As String) As Boolean
        config = New configMonitor()
        assert(config.open(configfile), "cannot open configruation " + configfile)
        Return readconfig()
    End Function

    Private Function matchthreshold(ByVal name As String, ByVal process As vector(Of String)) As Int64
        assert(Not process Is Nothing, "process is nothing.")
        Dim i As Int64
        For i = 0 To process.size() - 1
            If fitfilter(process(i), name) Then
                Return i
            End If
        Next

        Return npos
    End Function

    Private Sub impl()
        perfcounter.CategoryName = performanceCounterCategories.processor
        perfcounter.CounterName = performanceCounterCounters.processorTime
        Dim i As Int64
        For i = 0 To Environment.ProcessorCount() - 1
            perfcounter.InstanceName = Convert.ToString(i)
            writeperfvalue(perfcounter)
        Next
        perfcounter.InstanceName = performanceCounterInstances.total
        writeperfvalue(perfcounter)

        perfcounter.CategoryName = performanceCounterCategories.memory
        perfcounter.CounterName = performanceCounterCounters.availableBytes
        perfcounter.InstanceName = ""
        writeperfvalue(perfcounter, , , True)
        If writeConsole Then
            writeConsoleline(performanceCounterCategories.memory + " Total Physical Memory " + _
                             memorySizeDisplay(totalPhysicalMemory()))
        End If

#If RETIRED Then
        perfcounter.CategoryName = performanceCounterCategories.process
        Dim procs() As Process = Nothing
        procs = Process.GetProcesses()
        Dim procindex As map(Of String, Int64) = Nothing
        procindex = New map(Of String, Int64)
        For Each proc As Process In procs
            Dim procname As String = Nothing
            procname = proc.ProcessName()
            If procindex(procname) = 0 Then
                perfcounter.InstanceName = procname
            Else
                perfcounter.InstanceName = procname + character.numberMark _
                                            + Convert.ToString(procindex(procname))
            End If
            procindex(procname) += 1
            perfcounter.CounterName = performanceCounterCounters.processorTime
            writeperfvalue(perfcounter, , , , threshold.process.processor * 100)
            perfcounter.CounterName = performanceCounterCounters.workingSet
            writeperfvalue(perfcounter, , , True, threshold.process.workingSet)
            perfcounter.CounterName = performanceCounterCounters.virtualBytes
            writeperfvalue(perfcounter, , , True, threshold.process.virtualBytes)
        Next
#End If
        Dim procs() As Process = Nothing
        procs = Process.GetProcesses()
        For Each proc As Process In procs
            Dim recordindex As Int64
            recordindex = matchthreshold(proc.ProcessName, recordThreshold.process)
            Dim killindex As Int64
            killindex = matchthreshold(proc.ProcessName, killThreshold.process)

            If recordindex <> npos OrElse killindex <> npos Then
                Dim processor As Double
                Dim workingSet As Double
                Dim virtualBytes As Double
                Dim name As String

                name = proc.ProcessName()
                processor = proc.TotalProcessorTime.Ticks() / (Now.Ticks() - proc.StartTime.Ticks())
                workingSet = proc.WorkingSet64()
                virtualBytes = proc.VirtualMemorySize64()

                If recordindex <> npos Then
                    If processor >= recordThreshold.processor(recordindex) AndAlso processor <= 1 Then
                        writeImpl(performanceCounterCategories.process + "#" + proc.ProcessName + " average", processor)
                    End If
                    If workingSet >= recordThreshold.workingSet(recordindex) Then
                        writeImpl(performanceCounterCategories.process + "#" + proc.ProcessName + _
                                  character.blank + performanceCounterCounters.workingSet, workingSet, True)
                    End If
                    If virtualBytes >= recordThreshold.virtualBytes(recordindex) Then
                        writeImpl(performanceCounterCategories.process + "#" + proc.ProcessName + _
                                  character.blank + performanceCounterCounters.virtualBytes, virtualBytes, True)
                    End If
                End If

                If killindex <> npos Then
                    If (processor >= killThreshold.processor(killindex) AndAlso processor <= 1) _
                        OrElse workingSet >= killThreshold.workingSet(killindex) _
                        OrElse virtualBytes >= killThreshold.virtualBytes(killindex) Then
                        writeConsoleline("kill heavy process " + proc.ProcessName)
                        closeProcess(proc)
                    End If
                End If
            End If
        Next
    End Sub

    Public Sub main(ByVal args() As String)
        On Error Resume Next
        Dim configfile As String = defaultConfigfile
        If args.Length() > 0 Then
            configfile = args(0)
        End If
        assert(loadconfig(configfile), "cannot load configuration " + configfile)

        perfcounter = New PerformanceCounter()
        Do
            impl()
            Dim i As Int64 = roundinterval
            If isdebugmode() Then
                i = 5
            End If
            While i > 0
                If keepalive AndAlso Not consoleCancelKeyPressed() Then
                    sleepSeconds(1)
                Else
                    Exit Do
                End If
                i -= 1
            End While
        Loop

        debugpause(True)
    End Sub

    Private Sub config_configChanged() Handles config.configChanged
        readconfig()
    End Sub
End Module
